package com.br.android.math.trigonometry.circle;

import java.text.DecimalFormat;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;

public class TrigonometricCircleActivity extends Activity {

	public static String	EXTRA_LAST_X	 = "last.x";
	public static String	EXTRA_LAST_Y	 = "last.y";

	private boolean	     rotateThreadRunning	= false;
	private boolean	     cancelRotateThread	 = false;
	private Handler	     handler	         = new Handler();
	private boolean	     isPlus	             = false;
	private boolean	     showSin;
	private boolean	     showCos;
	private boolean	     showTg;
	private boolean	     showNotableAngles;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		PreferenceManager.setDefaultValues(this, R.xml.preferences, false);

		/* load user preferences */
		SharedPreferences preference = PreferenceManager.getDefaultSharedPreferences(this);
		showSin = preference.getBoolean(getString(R.string.sen), false);
		showCos = preference.getBoolean(getString(R.string.cos), false);
		showTg = preference.getBoolean(getString(R.string.tg), false);
		showNotableAngles = preference.getBoolean(getString(R.string.angulos), false);

		setContentView(new Panel(this));
	}

	// /**
	// * Inflate the menu; this adds items to the action bar if it is present.
	// */
	// @Override
	// public boolean onCreateOptionsMenu(Menu menu) {
	// getMenuInflater().inflate(R.menu.menu_settings, menu);
	// return true;
	// }

	/**
	 * respond to menu item selection
	 */
	// public boolean onOptionsItemSelected(MenuItem item) {
	// // Handle item selection
	// switch (item.getItemId()) {
	// case R.id.menu_settings:
	// startActivity(new Intent(this, SettingsActivity.class));
	// finish(); // gambi com BACKpressed
	// return true;
	// default:
	// return super.onOptionsItemSelected(item);
	// }
	// }

	class Panel extends SurfaceView implements SurfaceHolder.Callback {
		private CanvasThread	thread;
		private Point		 centerPoint, circlePoint, touchPoint, lastCirclePoint = new Point();
		private final int		colorCircle		= Color.WHITE;
		private final int		colorAxes		= Color.GRAY;
		private final int		colorLine		= Color.RED;
		private final int		colorCos		= Color.rgb(0, 0, 200);		                  // TODO mudar
		                                                                                          // cores azul e
		                                                                                          // vermelho mto
		                                                                                          // ruim
		                                                                                          // (dinamico?)
		private final int		colorSin		= Color.rgb(0, 255, 0);
		private final int		colorTg		    = Color.rgb(255, 255, 0);
		private final int		colorText		= Color.WHITE;
		private final int		colorBackground	= Color.BLACK;
		private int		     _radius;
		private int		     _radiusMin;
		private int		     _squarePlus;		                                                  // rect increase
		                                                                                          // size for
		                                                                                          // tought
		private Canvas		 canvas;
		private int		     _degrees, _lastDegrees, _tgY, _lastTgY, _arcsPositionX, _arcsPositionY, _arcsSpace,
		        _arcsTextSize;
		private double		 _radDegrees		= 0, _lastRadDegrees = 0;
		private boolean		 _start		        = true;
		private boolean		 tgDefined		    = false;
		private RectF		 btnPlus		    = new RectF(), btnMinus = new RectF(), btnSettings = new RectF();

		@Override
		public void onDraw(Canvas canvas) {
			this.canvas = canvas;

			/* Inicio para setar variáveis única vez */
			if (_start) {
				/* Tamanho das fontes e posição */
				// _arcsPositionX = 13*canvas.getWidth() / 20; //27;//TODO deixar posicao dos textos dinamica com a
				// tela
				// _arcsPositionY = 3*canvas.getHeight() / 4; //80;
				// _arcsSpace = 20;
				// _arcsTextSize = 18; //canvas.getHeight() / 10; //30; //TODO deixar tamanho do texto dinamico com
				// a resolucao da tela

				// Convert the dips to pixels
				_arcsPositionX = canvas.getWidth() - dipsToPixels(170);
				_arcsPositionY = canvas.getHeight() - dipsToPixels(70);
				_arcsSpace = dipsToPixels(20);
				_arcsTextSize = dipsToPixels(18);

				/* Centro do círculo */
				_radius = canvas.getHeight() / 2;// 220
				centerPoint = new Point(_radius, _radius);// X deslocado para a esquerda
				_radiusMin = dipsToPixels(20);
				_squarePlus = _radius / 16;
				circlePoint = new Point(centerPoint.x + _radius, centerPoint.y);

				SharedPreferences preference = PreferenceManager
				        .getDefaultSharedPreferences(TrigonometricCircleActivity.this);
				// SharedPreferences.Editor editor = preference.edit();
				// editor.putInt("x", 0); //valor default para o lastCirclePoint
				// editor.putInt("y", 0);
				// editor.commit();

				// SharedPreferences preference = PreferenceManager
				// .getDefaultSharedPreferences(TrigonometricCircleActivity.this);
				lastCirclePoint.x = preference.getInt("x", 1);
				lastCirclePoint.y = preference.getInt("y", 1);

				// if (getIntent().getExtras() != null) {
				// lastCirclePoint.x = getIntent().getExtras().getInt(
				// EXTRA_LAST_X);
				// lastCirclePoint.y = getIntent().getExtras().getInt(
				// EXTRA_LAST_Y);
				// }

				/* Linha inicial no angulo ZERO */
				// lastCirclePoint =
				touchPoint = circlePoint;
				_tgY = centerPoint.y;
				_start = false;
			}

			drawEverything();
		}

		/* Renderiza todos os elementos da tela */
		private void drawEverything() {
			/* Para controlar as cores */
			Paint paint = new Paint();
			paint.setAntiAlias(true);

			/* Cor da tela de fundo */
			canvas.drawColor(colorBackground);

			/* Ângulos notáveis */
			drawAngles();

			/* Eixos X e Y de referencia */
			paint.setStrokeWidth(2);
			paint.setPathEffect(null);
			paint.setColor(colorAxes);
			canvas.drawLine(0, centerPoint.y, centerPoint.x * 2, centerPoint.y, paint); // X (+50 para deixar = ao
			                                                                            // recf)
			canvas.drawLine(centerPoint.x, 0, centerPoint.x, centerPoint.y * 2, paint); // Y
			/* X e Y nas retas */
			paint.setTextSize(23);
			canvas.drawText("X", centerPoint.x + _radius + 20, centerPoint.y + 25, paint);
			canvas.drawText("Y", centerPoint.x + 15, 35, paint);

			/* Desenha os botões */
			drawButtons();

			/* Desenha o arco central para referência angular */
			RectF oval = new RectF();
			Paint arcPaint = new Paint();
			arcPaint.setColor(colorAxes);
			arcPaint.setStrokeWidth(5);
			arcPaint.setStyle(Paint.Style.FILL);
			oval.set(centerPoint.x - _radiusMin, centerPoint.y - _radiusMin, centerPoint.x + _radiusMin, centerPoint.y
			        + _radiusMin);
			canvas.drawArc(oval, 0, _lastDegrees, true, arcPaint);

			/* Desenha a linha de referência angular */
			paint.setColor(colorLine);
			paint.setStrokeWidth(3);
			paint.setStyle(Paint.Style.STROKE);
			Path path = new Path();
			path.moveTo(centerPoint.x, centerPoint.y);
			path.lineTo(lastCirclePoint.x, lastCirclePoint.y);
			canvas.drawPath(path, paint);

			/* Define retangulo delimitando área válida de toque */
			RectF f = new RectF(centerPoint.x - _radius, 0, centerPoint.x + _radius + _squarePlus, centerPoint.y * 2);
			/** Mostra retangulo para desenvolvimento */
			arcPaint.setColor(Color.WHITE);
			arcPaint.setStyle(Paint.Style.STROKE);
			canvas.drawRect(f, arcPaint);

			/*
			 * Guarda somente o toque dentro da area valida para renderizar elementos
			 */
			if (f.contains(touchPoint.x, touchPoint.y)) {
				lastCirclePoint = new Point();
				lastCirclePoint.x = circlePoint.x;
				lastCirclePoint.y = circlePoint.y;
				_lastDegrees = _degrees;
				_lastRadDegrees = _radDegrees;
				_lastTgY = _tgY;
			}

			/* Projecões */
			drawProjections();

			/* Valores das funções */
			paint = new Paint();
			paint.setAntiAlias(true);
			paint.setTextSize(30);
			DecimalFormat df = new DecimalFormat("#.####");
			StringBuffer sin, cos, tg, deg;
			deg = new StringBuffer();
			sin = new StringBuffer(df.format(Math.sin(-1 * _lastRadDegrees)));
			cos = new StringBuffer(df.format(Math.cos(_lastRadDegrees)));
			tgDefined = _lastDegrees != -270 && _lastDegrees != -90;
			tg = new StringBuffer((tgDefined ? df.format(Math.tan(-1 * _lastRadDegrees)) : ""));

			/* Para os ângulos notáveis, representação em fração, padrao BR */
			defineNotableAngles(sin, cos, tg, deg);

			paint = new Paint();
			paint.setAntiAlias(true);

			/* Valor do ângulo (arco) */
			canvas.restore();
			paint.setStrokeWidth(1);
			paint.setTextSize(_arcsTextSize);
			paint.setStyle(Paint.Style.FILL);
			paint.setColor(colorText);
			canvas.drawText("Arco: " + -_lastDegrees + "° " + deg, _arcsPositionX, _arcsPositionY - _arcsSpace, paint);

			paint.setTextSize(_arcsTextSize);
			paint.setColor(colorSin);
			String arco = deg.length() != 0 ? deg.toString() : "(" + -_lastDegrees + "°)";
			canvas.drawText("Sen " + arco + ": " + sin, _arcsPositionX, _arcsPositionY + _arcsSpace, paint);
			paint.setColor(colorCos);
			canvas.drawText("Cos " + arco + ": " + cos, _arcsPositionX, _arcsPositionY + 2 * _arcsSpace, paint);
			paint.setColor(colorTg);
			canvas.drawText("Tg   " + arco + ": " + tg, _arcsPositionX, _arcsPositionY + 3 * _arcsSpace, paint);

			/* Círculo unitário */
			paint.setColor(colorCircle);
			paint.setStrokeWidth(4);
			paint.setStyle(Paint.Style.STROKE);
			canvas.drawCircle(centerPoint.x, centerPoint.y, _radius, paint);

			/* Valores para desenvolvimento */
			// canvas.drawText("T:" + touchPoint.toString(), canvas.getWidth() -
			// 500, 500, paint);
			// canvas.drawText("C:" + circlePoint.toString(), canvas.getWidth()
			// -500, 530, paint);
			// canvas.drawText("rad: " + _lastRadDegrees, canvas.getWidth() -
			// 500, 440, paint);
			// Double x = centerPoint.x + _radius * Math.cos(_lastRadDegrees);
			// Double y = centerPoint.y + _radius * Math.sin(_lastRadDegrees);
			// canvas.drawText(" x: " + x.intValue() +" y: " + y.intValue() ,
			// canvas.getWidth() - 500, 470, paint);

		}

		/**
		 * Para os ângulos notáveis, valores em frações
		 * 
		 * @param sin
		 * @param cos
		 * @param tg
		 * @param deg
		 *            Criado em 29/08/2012
		 */
		private void defineNotableAngles(StringBuffer sin, StringBuffer cos, StringBuffer tg, StringBuffer deg) {
			switch (-_lastDegrees) {
			case 0:
				deg.replace(0, 10, "(0 rad)");
				cos.replace(0, 10, "1");
				sin.replace(0, 10, "0");
				tg.replace(0, 10, "0");
			break;
			case 30:
				deg.replace(0, 10, "(π/6 rad)");
				sin.replace(0, 10, "1/2");
				cos.replace(0, 10, "√3/2");
				tg.replace(0, 10, "√3/3");
			break;
			case 45:
				deg.replace(0, 10, "(π/4 rad)");
				sin.replace(0, 10, "√2/2");
				cos.replace(0, 10, "√2/2");
				tg.replace(0, 10, "1");
			break;
			case 60:
				deg.replace(0, 10, "(π/3 rad)");
				sin.replace(0, 10, "√3/2");
				cos.replace(0, 10, "1/2");
				tg.replace(0, 10, "√3");
			break;
			case 90:
				deg.replace(0, 10, "(π/2 rad)");
				cos.replace(0, 10, "0");
				sin.replace(0, 10, "1");
				tg.replace(0, 10, "ND");
			break;
			case 120: /* 60 */
				deg.replace(0, 10, "(2π/3 rad)");
				sin.replace(0, 10, "√3/2");
				cos.replace(0, 10, "-1/2");
				tg.replace(0, 10, "-√3");
			break;
			case 135: /* 45 */
				deg.replace(0, 10, "(3π/4 rad)");
				sin.replace(0, 10, "√2/2");
				cos.replace(0, 10, "-√2/2");
				tg.replace(0, 10, "-1");
			break;
			case 150: /* 30 */
				deg.replace(0, 10, "(5π/6 rad)");
				sin.replace(0, 10, "1/2");
				cos.replace(0, 10, "-√3/2");
				tg.replace(0, 10, "-√3/3");
			break;
			case 180:
				deg.replace(0, 10, "(π rad)");
				cos.replace(0, 10, "-1");
				sin.replace(0, 10, "0");
				tg.replace(0, 10, "0");
			break;
			case 210: /* 30 */
				deg.replace(0, 10, "(7π/6 rad)");
				sin.replace(0, 10, "-1/2");
				cos.replace(0, 10, "-√3/2");
				tg.replace(0, 10, "√3/3");
			break;
			case 225: /* 45 */
				deg.replace(0, 10, "(5π/4 rad)");
				sin.replace(0, 10, "-√2/2");
				cos.replace(0, 10, "-√2/2");
				tg.replace(0, 10, "1");
			break;
			case 240: /* 60 */
				deg.replace(0, 10, "(4π/3 rad)");
				sin.replace(0, 10, "-√3/2");
				cos.replace(0, 10, "-1/2");
				tg.replace(0, 10, "√3");
			break;
			case 270:
				deg.replace(0, 10, "(3π/2 rad)");
				cos.replace(0, 10, "0");
				sin.replace(0, 10, "-1");
				tg.replace(0, 10, "Não definida");
			break;
			case 300: /* 60 */
				deg.replace(0, 10, "(5π/3 rad)");
				sin.replace(0, 10, "-√3/2");
				cos.replace(0, 10, "1/2");
				tg.replace(0, 10, "-√3");
			break;
			case 315: /* 45 */
				deg.replace(0, 10, "(7π/4 rad)");
				sin.replace(0, 10, "-√2/2");
				cos.replace(0, 10, "√2/2");
				tg.replace(0, 10, "-1");
			break;
			case 330: /* 30 */
				deg.replace(0, 10, "(11π/6 rad)");
				sin.replace(0, 10, "-1/2");
				cos.replace(0, 10, "√3/2");
				tg.replace(0, 10, "-√3/3");
			}
		}

		/**
		 * Renderiza as projeções Sin,Cos,Tg
		 * 
		 * @param paint
		 *            Criado em 29/08/2012
		 */
		private void drawProjections() {
			Paint paint = new Paint();
			Paint paintTraced = new Paint();
			paintTraced.setStrokeWidth(1);
			paintTraced.setColor(colorAxes);
			paintTraced.setPathEffect(new DashPathEffect(new float[] { 5, 5 }, 0));
			paint.setStrokeWidth(1);
			paint.setAntiAlias(true);
			paint.setColor(colorSin);
			if (showSin) {
				canvas.drawLine(centerPoint.x, centerPoint.y, centerPoint.x, lastCirclePoint.y, paint);
				canvas.drawLine(lastCirclePoint.x, lastCirclePoint.y, centerPoint.x, lastCirclePoint.y, paintTraced);
			}
			paint = new Paint(); // limpando Paint
			paint.setColor(colorCos);
			if (showCos) {
				canvas.drawLine(centerPoint.x, centerPoint.y, lastCirclePoint.x, centerPoint.y, paint);
				canvas.drawLine(lastCirclePoint.x, lastCirclePoint.y, lastCirclePoint.x, centerPoint.y, paintTraced);
			}
			paint.setColor(colorTg);
			if (showTg && tgDefined) {
				canvas.drawLine(centerPoint.x + _radius, centerPoint.y, centerPoint.x + _radius, _lastTgY, paint);
				canvas.drawLine(lastCirclePoint.x, lastCirclePoint.y, centerPoint.x + _radius, _lastTgY, paintTraced);
			}
		}

		/**
		 * Renderiza os botões
		 * 
		 * @param paint
		 *            Criado em 29/08/2012
		 */
		private void drawButtons() {
			int bottom = 87;
			int space = 8;
			int left = canvas.getWidth() - 84;
			int right = canvas.getWidth() - 1;
			Paint txPaint = new Paint();
			txPaint.setColor(colorText);
			txPaint.setStyle(Paint.Style.FILL);
			txPaint.setAntiAlias(true);
			txPaint.setStrokeWidth(2);
			txPaint.setTextAlign(Align.CENTER);
			txPaint.setTextSize(25);
			Paint paint = new Paint();
			paint.setAntiAlias(true);
			paint.setColor(colorCircle);
			paint.setStyle(Paint.Style.STROKE);
			paint.setStrokeWidth(2);

			/* Botão + * */
			txPaint.setTextSize(25);
			btnPlus = new RectF(left, space, right, bottom);
			canvas.drawRect(btnPlus, paint);
			txPaint.setColor(colorText);
			txPaint.setTextSize(40);
			txPaint.setStrokeWidth(8);
			canvas.drawText("+", btnPlus.centerX(), btnPlus.centerY(), txPaint);

			/* Botão - * */
			btnMinus = new RectF(left, bottom + space, right, 2 * bottom);
			canvas.drawRect(btnMinus, paint);
			txPaint.setTextSize(40);
			txPaint.setStrokeWidth(8);
			canvas.drawText("-", btnMinus.centerX(), btnMinus.centerY(), txPaint);

			/* Botão Projecoes * */
			btnSettings = new RectF(left - space - (right - left), space, left - space, bottom);
			canvas.drawRect(btnSettings, paint);
			txPaint.setTextSize(_arcsTextSize - 2);
			txPaint.setStrokeWidth(8);
			canvas.drawText("Projeções", btnSettings.centerX(), btnSettings.centerY(), txPaint);
		}

		/**
		 * Renderiza os ângulos Calculo de X/Y pela equação do circulo paramétrica
		 * 
		 * @param canvas
		 * @param paint
		 *            Criado em 29/08/2012
		 */
		private void drawAngles() {
			Paint paint = new Paint();
			paint.setAntiAlias(true);
			paint.setColor(colorAxes);
			paint.setStrokeWidth(2);

			/* Ângulos de 10° em 10° */
			paint.setPathEffect(new DashPathEffect(new float[] { 20, _radius - 7 }, 20)); // _radius-8
			for (int i = 10; i < 360; i += 10) {
				// int x = new Double(centerPoint.x + _radius *
				// Math.cos(Math.toRadians(i))).intValue();
				// int y = new Double(centerPoint.y + _radius *
				// Math.sin(Math.toRadians(i))).intValue();
				int x = Double.valueOf(centerPoint.x + _radius * Math.cos(Math.toRadians(i))).intValue();
				int y = Double.valueOf(centerPoint.y + _radius * Math.sin(Math.toRadians(i))).intValue();
				this.canvas.drawLine(centerPoint.x, centerPoint.y, x, y, paint);
			}

			if (showNotableAngles) {
				/* 30° */
				paint.setPathEffect(new DashPathEffect(new float[] { 20, _radius - 20 }, 20)); // _radius-20
				for (int i = 30; i < 360; i += 30) {
					int x = Double.valueOf(centerPoint.x + _radius * Math.cos(Math.toRadians(i))).intValue();
					int y = Double.valueOf(centerPoint.y + _radius * Math.sin(Math.toRadians(i))).intValue();
					// int x = new Double(centerPoint.x + _radius *
					// Math.cos(Math.toRadians(i))).intValue();
					// int y = new Double(centerPoint.y + _radius *
					// Math.sin(Math.toRadians(i))).intValue();
					this.canvas.drawLine(centerPoint.x, centerPoint.y, x, y, paint);
				}

				/* 45° */
				paint.setPathEffect(new DashPathEffect(new float[] { 40, _radius - 40 }, 40)); // _radius-30
				for (int i = 45; i < 360; i += 45) {
					// int x = new Double(centerPoint.x + _radius *
					// Math.cos(Math.toRadians(i))).intValue();
					// int y = new Double(centerPoint.y + _radius *
					// Math.sin(Math.toRadians(i))).intValue();
					int x = Double.valueOf(centerPoint.x + _radius * Math.cos(Math.toRadians(i))).intValue();
					int y = Double.valueOf(centerPoint.y + _radius * Math.sin(Math.toRadians(i))).intValue();
					this.canvas.drawLine(centerPoint.x, centerPoint.y, x, y, paint);
				}
			}
		}

		/**
		 * Chamado aos eventos de toque na tela
		 */
		@Override
		public boolean onTouchEvent(MotionEvent event) {
			touchPoint = new Point((int) event.getX(), (int) event.getY());

			switch (event.getAction()) {
			case MotionEvent.ACTION_DOWN:
				if (btnSettings.contains(touchPoint.x, touchPoint.y)) {
					Intent intent = new Intent(TrigonometricCircleActivity.this, SettingsActivity.class);// XXX
					// intent.putExtra(SettingsActivity.EXTRA_LAST_X, lastCirclePoint.x);
					// intent.putExtra(SettingsActivity.EXTRA_LAST_Y, lastCirclePoint.y);
					SharedPreferences preference = PreferenceManager
					        .getDefaultSharedPreferences(TrigonometricCircleActivity.this);
					preference.edit().putInt("x", lastCirclePoint.x);
					preference.edit().putInt("y", lastCirclePoint.y);
					preference.edit().commit();

					startActivity(intent);
					break;
				} else if (btnPlus.contains(touchPoint.x, touchPoint.y)) {
					isPlus = false;
					onButtonDown();
					break;
				} else if (btnMinus.contains(touchPoint.x, touchPoint.y)) {
					isPlus = true;
					onButtonDown();
					break;
				}
				doOnTouch(false);
			break;
			case MotionEvent.ACTION_MOVE:
				doOnTouch(false);
				/* Se mover para fora dos botões, para thread */
				if (!btnPlus.contains(touchPoint.x, touchPoint.y) || !btnMinus.contains(touchPoint.x, touchPoint.y)) {
					stopRotateThread();
				}
			break;
			case MotionEvent.ACTION_UP:
				stopRotateThread();
			break;
			}
			return true;
		}

		private void doOnTouch(boolean isButton) {
			/* Se for o movimento no próprio círculo */
			if (!isButton) {
				/* calculo do angulo entre duas linhas */
				_degrees = angleBetween2Lines(centerPoint.x, centerPoint.y, centerPoint.x + 200, centerPoint.y,
				        touchPoint.x, touchPoint.y);
				calculateLinePositions(touchPoint.x, touchPoint.y);
				calculateLineTgValueY(circlePoint.x, circlePoint.y);
			} else { /* Senão são os botões +/- */
				/** Calcular X/Y pela equação do circulo */
				lastCirclePoint.x = Double.valueOf(centerPoint.x + _radius * Math.cos(_lastRadDegrees)).intValue();
				// new Double(centerPoint.x + _radius *
				// Math.cos(_lastRadDegrees)).intValue();
				lastCirclePoint.y = Double.valueOf(centerPoint.y + _radius * Math.sin(_lastRadDegrees)).intValue();
				// new Double(centerPoint.y + _radius *
				// Math.sin(_lastRadDegrees)).intValue();
				/* Correcao do ponto do circulo e TgY */
				calculateLineTgValueY(lastCirclePoint.x, lastCirclePoint.y);
				_lastTgY = _tgY;

				_lastDegrees = angleBetween2Lines(centerPoint.x, centerPoint.y, centerPoint.x + 200, centerPoint.y,
				        lastCirclePoint.x, lastCirclePoint.y);
			}
			this.invalidate();
		}

		/**
		 * Calcula o valor de Y da linha Tangente Criado em 25/08/2012
		 */
		private void calculateLineTgValueY(int x, int y) {
			Double B, K;

			K = (double) (x - centerPoint.x) / _radius;
			if (K != 0) {
				B = (y - centerPoint.y) / K;
				_tgY = centerPoint.y + B.intValue();
			} else {
				_tgY = -40000; // protecao (aproximação) para nao travar no
				               // infinito (angulo 90 e 270 graus)
			}
		}

		/**
		 * Calcula a possicao X,Y da linha para ela ter um tamanho constante no circulo inteiro. Utilizando
		 * triangulos semelhantes Criado em 22/08/2012
		 */
		private void calculateLinePositions(int x, int y) {
			int cx, cy, tx, ty;
			int a, b, R;
			Double c;

			cx = centerPoint.x; // centro do círculo
			cy = centerPoint.y;
			tx = x; // touchPoint.x; //ponto tocado na tela
			ty = y; // touchPoint.y;
			R = _radius; // lado 3 do triangulo 2

			a = tx - cx; // lado 1 do triangulo 1
			b = ty - cy; // lado 2 do triangulo 1
			c = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2)); // lado 3 do
			                                                // triangulo 1

			final Double K = R / c;
			// pontos no circulo
			circlePoint.x = cx + (int) (K * a); // ponto do centro + (proporcao
			                                    // para aumento * tamanho lado
			                                    // triangulo 1)
			circlePoint.y = cy + (int) (K * b);
		}

		/**
		 * Calcula o angulo entre as duas linhas Linha 1: (centerX,centerY) -> (x1,y1) Linha 2: (centerX,centerY)
		 * -> (x2,y2)
		 * 
		 * @param centerX
		 * @param centerY
		 * @param x1
		 * @param y1
		 * @param x2
		 * @param y2
		 * @return Criado em 17/08/2012
		 */
		private int angleBetween2Lines(float centerX, float centerY, float x1, float y1, float x2, float y2) {
			double angle1 = Math.atan2(y1 - centerY, x1 - centerX);
			double angle2 = Math.atan2(y2 - centerY, x2 - centerX);

			double angle = Math.toDegrees(angle1 - angle2) * -1;

			// correcao pra retornar somente angulos negativos (para a rotação
			// ser feita no sentido anti-horario)
			Double a = angle > 0 ? angle - 360 : angle;
			_radDegrees = Math.toRadians(a);
			return a.intValue();
		}

		/**
		 * Verifica se os pontos estao dentro do circulo definido pelos pontos
		 * 
		 * @param x
		 * @param y
		 * @param circleCenterX
		 * @param circleCenterY
		 * @param circleRadius
		 * @return Criado em 24/08/2012
		 */
		@SuppressWarnings("unused")
		// inCircle(touchPoint.x, touchPoint.y, centerPoint.x, centerPoint.y,
		// _radius)
		private boolean inCircle(float x, float y, float circleCenterX, float circleCenterY, float circleRadius) {
			double dx = Math.pow(x - circleCenterX, 2);
			double dy = Math.pow(y - circleCenterY, 2);

			if ((dx + dy) < Math.pow(circleRadius, 2)) {
				return true;
			} else {
				return false;
			}
		}

		/**
		 * Ao apertar botao, inicia thread de mudanca de angulo Criado em 28/08/2012
		 */
		private void onButtonDown() {
			if (!rotateThreadRunning)
				startRotateThread();
		}

		/**
		 * Para a thread de mudança de angulo Criado em 28/08/2012
		 */
		private void stopRotateThread() {
			if (rotateThreadRunning)
				cancelRotateThread = true;
		}

		/**
		 * Thread de mudança de angulo Criado em 28/08/2012
		 */
		private void startRotateThread() {

			Thread r = new Thread() {

				@Override
				public void run() {
					try {

						rotateThreadRunning = true;
						while (!cancelRotateThread) {

							handler.post(new Runnable() {
								@Override
								public void run() {
									if (isPlus) {
										_lastRadDegrees = _lastRadDegrees + 0.01D;
									} else {
										_lastRadDegrees = _lastRadDegrees - 0.01D;
									}
									doOnTouch(true);
								}
							});

							try {
								Thread.sleep(100);
							} catch (InterruptedException e) {
								throw new RuntimeException("------ Problema na thread rotate ------", e);
							}
						}
					} finally {
						rotateThreadRunning = false;
						cancelRotateThread = false;
					}
				}
			};

			r.start();
		}

		@Override
		public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
		}

		@Override
		public void surfaceCreated(SurfaceHolder holder) {
			thread.setRunning(true);
			thread.start();
		}

		public Panel(Context context) {
			super(context);
			getHolder().addCallback(this);
			thread = new CanvasThread(getHolder(), this);
			setFocusable(true);
		}

		@Override
		public void surfaceDestroyed(SurfaceHolder holder) {
			/*
			 * É preciso mandar a thread desligar e esperar até terminar, senão pode ocorrer um toque na tela e
			 * travar
			 */
			boolean retry = true;
			thread.setRunning(false);
			while (retry) {
				try {
					thread.join();
					retry = false;
					finish(); // termina activity
				} catch (InterruptedException e) {
					// we will try it again and again...
				}
			}
		}
	}

	class CanvasThread extends Thread {
		/*
		 * In order to draw to the Surface Canvas from within your second thread, you must pass the thread your
		 * SurfaceHandler and retrieve the Canvas with lockCanvas().
		 */
		private SurfaceHolder	_surfaceHolder;
		private Panel		  _panel;
		private boolean		  _run	= false;

		public CanvasThread(SurfaceHolder surfaceHolder, Panel panel) {
			_surfaceHolder = surfaceHolder;
			_panel = panel;
		}

		@SuppressLint("WrongCall")
		@Override
		public void run() {
			Canvas canvas;
			while (_run) {
				canvas = null;
				try {
					canvas = _surfaceHolder.lockCanvas(null);
					synchronized (_surfaceHolder) {
						/*
						 * You can now take the Canvas given to you by the SurfaceHolder and do your necessary
						 * drawing upon it
						 */
						_panel.onDraw(canvas);
					}
				} finally {
					// do this in a finally so that if an exception is thrown
					// during the above, we don't leave the Surface in an
					// inconsistent state
					if (canvas != null) {
						/*
						 * Once you're done drawing with the Canvas, call unlockCanvasAndPost(), passing it your
						 * Canvas object. The Surface will now draw the Canvas as you left it. Perform this
						 * sequence of locking and unlocking the canvas each time you want to redraw
						 */
						_surfaceHolder.unlockCanvasAndPost(canvas);
					}
				}
			}
		}

		public void setRunning(boolean isRunning) {
			this._run = isRunning;
		}

		public SurfaceHolder getSurfaceHolder() {
			return this._surfaceHolder;
		}
	}

	/**
	 * Convert the dips (Device independt pixels) to pixels Fica o mesmo tamanho independente da resolucao da tela
	 */
	private int dipsToPixels(int dipValue) {
		return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, getResources()
		        .getDisplayMetrics());
	}

}
